package pl.asoft.nbp.dao.impl;

import org.hibernate.Query;
import org.hibernate.SessionFactory;
import org.hibernate.classic.Session;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import pl.asoft.nbp.dao.PersistentObjectDAO;
import pl.asoft.nbp.model.PersistentObject;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Created by rbiedrawa on 2014-12-02.
 */

@Transactional(readOnly = true)
public abstract class PersistentObjectDAOImpl<T extends PersistentObject<ID>, ID extends Serializable> implements PersistentObjectDAO<T, ID> {
    protected final Class<T> persistentClass;

    @Autowired
    protected SessionFactory sf;

    public PersistentObjectDAOImpl() {
        ParameterizedType pt = (ParameterizedType) getClass().getGenericSuperclass();
        persistentClass = (Class) pt.getActualTypeArguments()[0];
    }

    @Override
    @Transactional(readOnly = false)
    public T save(T entity) {
        sf.getCurrentSession().save(entity);
        return entity;
    }

    @Override
    @Transactional(readOnly = false)
    public void save(List<T> listOfEntitys) {
        Session session = sf.getCurrentSession();

        for (int i = 0; i < listOfEntitys.size(); i++) {
            T t = listOfEntitys.get(i);
            session.save(t);
            if (i % 20 == 0) { // pasowalo by to potestowac wszystko zalezy od pamieci mozna by bylo zwiekszyc zeby flusha nie wolac
                session.flush();
                session.clear();
            }
        }

    }

    @Override
    public T find(final ID id) {
        return (T) sf.getCurrentSession().get(persistentClass, id);
    }

    @Override
    public List<T> findAll() {
        return sf.getCurrentSession().createCriteria(persistentClass).list();
    }

    @Override
    @Transactional(readOnly = false)
    public T update(T entity) {
        sf.getCurrentSession().update(entity);
        sf.getCurrentSession().flush();
        sf.getCurrentSession().refresh(entity);
        return entity;
    }

    @Override
    @Transactional(readOnly = false)
    public void delete(ID id) {
        T result = find(id);
        sf.getCurrentSession().delete(result);
    }

    @Override
    public List findByNamedQuery(String queryName) {
        return sf.getCurrentSession().getNamedQuery(queryName).list();
    }

    @Override
    public List findByNamedQuery(String queryName, int resultLimit) {
        return sf.getCurrentSession().getNamedQuery(queryName).setMaxResults(resultLimit).list();
    }

    @Override
    public List findByNamedQuery(String namedQueryName, Map parameters) {
        return findByNamedQuery(namedQueryName, parameters, 0);
    }

    @Override
    public List findByNamedQuery(String namedQueryName, Map parameters, int resultLimit) {
        Set<Map.Entry<String, Object>> rawParameters = parameters.entrySet();
        Query query = sf.getCurrentSession().getNamedQuery(namedQueryName);

        for (Map.Entry<String, Object> entry : rawParameters) {
            if (entry.getValue() instanceof Date) {
                query.setTimestamp(entry.getKey(), (Date) entry.getValue());
            } else {
                query.setParameter(entry.getKey(), entry.getValue());
            }
        }

        if (resultLimit > 0) {
            query.setMaxResults(resultLimit);
        }

        return query.list();
    }

    @Override
    public List findByNativeQuery(String nativeQuery, Class clazz) {
        return sf.getCurrentSession().createSQLQuery(nativeQuery).addEntity(clazz).list();
    }

    @Override
    public List<Object[]> findByNativeQuery(String nativeQuery) {
        return sf.getCurrentSession().createSQLQuery(nativeQuery).list();
    }
}
